package top.malaoshi.webmvc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import top.malaoshi.webmvc.factory.DefaultObjectFactory;
import top.malaoshi.webmvc.info.BeanAnnotationInfo;
import top.malaoshi.webmvc.info.ClassInfo;
import top.malaoshi.webmvc.info.FieldInfo;
import top.malaoshi.webmvc.info.MethodInfo;
import top.malaoshi.webmvc.utils.ClassUtil;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 解析类、类中方法、类中的成员变量
 */
public class ClassAndMethodAndFieldResolver {



    /**
     * 解析class，放入到 beanInfos 中
     */
    public ClassInfo resolve(Class<?> clazz)  {
        ClassInfo classInfo = new ClassInfo();

        String name = null;
        if (clazz.isAnnotationPresent(Controller.class)) {
            Controller controller = clazz.getAnnotation(Controller.class);
            name = controller.value();
            classInfo.setComponentAnnotation(controller.getClass());
        } else if (clazz.isAnnotationPresent(RestController.class)) {
            RestController annotation = clazz.getAnnotation(RestController.class);
            name = annotation.value();
            classInfo.setComponentAnnotation(annotation.getClass());
        } else if (clazz.isAnnotationPresent(Component.class)) {
            Component annotation = clazz.getAnnotation(Component.class);
            name = annotation.value();
            classInfo.setComponentAnnotation(annotation.getClass());
        } else if (clazz.isAnnotationPresent(Service.class)) {
            Service annotation = clazz.getAnnotation(Service.class);
            name = annotation.value();
            classInfo.setComponentAnnotation(annotation.getClass());
        } else if (clazz.isAnnotationPresent(Configuration.class)) {
            Configuration annotation = clazz.getAnnotation(Configuration.class);
            name = annotation.value();
            classInfo.setConfigurationAnnotation(annotation);
        } else if (clazz.isAnnotationPresent(ControllerAdvice.class) || clazz.isAnnotationPresent(RestControllerAdvice.class)) {
//            ControllerAdvice annotation = clazz.getAnnotation(ControllerAdvice.class);
            name = clazz.getName();
//            classInfo.setComponentAnnotation(annotation.getClass());
        }else {
            return null;
        }
        // 指定配置文件注解
        if (clazz.isAnnotationPresent(PropertySource.class)) {
            PropertySource annotation = clazz.getAnnotation(PropertySource.class);
            classInfo.setPropertySource(annotation);
        }
        // 如果没有配置注解的 value 属性，就将类名作为实例名，首字母小写
        if ("".equals(name)) {
            name = ClassUtil.lowerFirstChar(clazz.getSimpleName());
        }
        classInfo.setName(name);
        classInfo.setClazz(clazz);
        classInfo.setObjectFactory(new DefaultObjectFactory(classInfo));

        resolveMethod(classInfo);
        resolveField(classInfo);
        resolveSuperField(classInfo);
        return classInfo;

    }

    /**
     * 解析方法
     * @param classInfo
     */
    private void resolveMethod(ClassInfo classInfo){
        Class<?> beanClass = classInfo.getClazz();

        Method[] methods = beanClass.getDeclaredMethods();
        for(Method method : methods){
            MethodInfo methodInfo = new MethodInfo(method);
            if(method.isAnnotationPresent(Bean.class)){
                resolveBeanAnnotationMethod(classInfo,method,methodInfo);
            }

        }


    }

    /**
     * 解析 @Bean 注解修饰的方法
     * 解析后，@Bean 注解的 value、name是肯定有值的
     * @param method
     * @param methodInfo
     */
    private void resolveBeanAnnotationMethod(ClassInfo classInfo,Method method , MethodInfo methodInfo ){
        Bean ann = method.getAnnotation(Bean.class);
        String[] value = ann.value();
        String[] name = ann.name();

        BeanAnnotationInfo beanAnnotationInfo = new BeanAnnotationInfo();
        boolean hasValue = false;
        if(value!=null && value.length > 0){
            beanAnnotationInfo.setValue(value);
            hasValue = true;
        }
        boolean hasName = false;
        if(name!=null && name.length > 0){
            beanAnnotationInfo.setName(name);
            hasName = true;
        }
        if(hasValue && !hasName){
            // 如果有 value，没有name，就将value赋值给name
            beanAnnotationInfo.setName(value);
        }else if(!hasValue && hasName){
            // 如果没有 value，有name，就将name赋值给value
            beanAnnotationInfo.setValue(name);
        }else if(!hasValue && !hasName){
            // 如果没有value、没有name，就将方法名（首字母小写） 作为value、name
            String beanName = ClassUtil.lowerFirstChar(method.getName());
            String[] names = {beanName};
            beanAnnotationInfo.setValue(names);
            beanAnnotationInfo.setName(names);
        }
        methodInfo.setBeanAnnotationInfo(beanAnnotationInfo);
        methodInfo.setClassInfo(classInfo);
        classInfo.addBeanAnnotationMethodInfo(methodInfo);
    }


    /**
     * 解析成员变量的所有注解
     * @param classInfo
     */
    private void resolveField(ClassInfo classInfo){
        Class clazz = classInfo.getClazz();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            FieldInfo fi = new FieldInfo(field);
            classInfo.addFieldInfo(fi);
            if (field.isAnnotationPresent(Autowired.class)) {
                Autowired ann = field.getAnnotation(Autowired.class);
                fi.setAutowired(ann);
            } else if (field.isAnnotationPresent(Resource.class)) {
                Resource ann = field.getAnnotation(Resource.class);
                fi.setResource(ann);
            } else if (field.isAnnotationPresent(Value.class)) {
                Value ann = field.getAnnotation(Value.class);
                fi.setValue(ann);
            }
        }
    }
    /**
     * 解析所有父类的成员变量
      */
    private void resolveSuperField(ClassInfo classInfo){
        Class clazz = classInfo.getClazz().getSuperclass();
        //向上循环  遍历父类
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                FieldInfo fi = new FieldInfo(field);
                classInfo.addFieldInfo(fi);
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired ann = field.getAnnotation(Autowired.class);
                    fi.setAutowired(ann);
                } else if (field.isAnnotationPresent(Resource.class)) {
                    Resource ann = field.getAnnotation(Resource.class);
                    fi.setResource(ann);
                } else if (field.isAnnotationPresent(Value.class)) {
                    Value ann = field.getAnnotation(Value.class);
                    fi.setValue(ann);
                }
            }
        }
    }
}
